
#include "alloc_context_impl.h"

#include <string.h>


static void *
find_suitable_block( AllocContext * c, size_t size )
{
    AtomBlock * b = c->impl->atoms.atom_block;
    for( ; b != 0; b = b->next )
    {
        if( (b->size + size) <= c->settings.atom_chunks_grow_size )
        {
            void *      result = b->buf + b->size;
            b->size += size;
            return result;
        }
    }

    return 0;
}

static void *
alloc_chunk( AllocContext * c, size_t size )
{
    void *      result;

    if( size > c->settings.atom_chunks_grow_size )
    {
        alloc_emit_error( c, QL_ERR_WRONG_CONF );
    }

    result = find_suitable_block( c, size );
    if( result == 0 )
    {
        AtomBlock *  b = c->impl->atoms.atom_block;
        AtomBlock *  new_b = ql_mallocz( c,
            (c->settings.atom_chunks_grow_size - 1) * sizeof(char) + sizeof(AtomBlock)
            );

        new_b->next = b;
        c->impl->atoms.atom_block = new_b;
    }
    result = find_suitable_block( c, size );

    assert( result != 0 );
    return result;
}

Obj * alloc_user_defined_obj( AllocContext * c, size_t size )
{
    Obj *       result = alloc_chunk( c, size );
    
    result->type = OBJ_USERDEFINED;
    
    return result;
}

IntNum * alloc_int_num( AllocContext * c, int val )
{
    IntNum *    result = alloc_chunk( c, sizeof(IntNum) );

    result->type = OBJ_INT_NUM;
    result->val = val;

    return result;
}

DblNum * alloc_dbl_num( AllocContext * c, double val )
{
    DblNum *    result = alloc_chunk( c, sizeof(DblNum) );

    result->type = OBJ_DBL_NUM;
    result->val = val;

    return result;
}

Str * alloc_str_len( AllocContext * c, const char * str, size_t str_size )
{
    Str *         result = alloc_chunk( c, sizeof(Str) + str_size );

    result->type = OBJ_STRING;
    memcpy( result->str, str, str_size );
    result->str[str_size] = 0;

    return result;
}

Str * alloc_str( AllocContext * c, const char * str )
{
    return alloc_str_len( c, str, strlen(str) );
}

void free_atoms_buffers( AllocContext * c )
{
    AtomBlock * b = c->impl->atoms.atom_block;
    
    while( b != 0 )
    {
        AtomBlock *  next_b = b->next;
        ql_free( b );
        b = next_b;
    }
}
